#include "USB_Load_Box.h"

/**************************************************************************
* Intro screen PROGMEM needs to be defined outside of program execution   *
***************************************************************************/
char const HyperTronix_Inc[] PROGMEM = "HyperTronix Inc.";
char const HTX_9000[] PROGMEM = "HTX-9000";

/**************************************************************************
*  Create up a soft I2C port with global scope                            *
***************************************************************************/
i2c_port_t i2cport_1;
/**************************************************************************
*                            Main                                         *
***************************************************************************/
int main(void)
{
/**************************************************************************
*  Create data structures                                                 *
***************************************************************************/
	static FILE			USB_stream;						// Create the USB Stream data
	static FILE			LCD_stream;						// Create the LCD Stream data
	IO_pointers_t		IO;								// Create the passable IO pointer
	char				str_in[MAX_IN_STR_LEN+1] = "";	// Incoming string
	int					str_len = 0;					// Length of incoming string
	uint8_t				cgram_chars[8][8];				// CGRAM characters [CHAR][5 BIT LINE IN CHAR]
	scpi_commands_P_t	commands_P[COMMAND_ARRAY_SIZE];	// Create the SCPI command array
	int					command_count;					// Used to check that commands were counted properly
	CDC_Device_CreateStream(&VirtualSerial_CDC_Interface, &USB_stream);
	fdev_setup_stream(&LCD_stream, LCD_Put_Char, NULL, _FDEV_SETUP_WRITE);

//	static FILE LCD_stream = FDEV_SETUP_STREAM(LCD_Put_Char, NULL, _FDEV_SETUP_WRITE);
//	to use this format, comment out the line "static FILE LCD_stream";
/**************************************************************************
*  Assign values to data structures                                       *
***************************************************************************/
	IO.USB_stream = &USB_stream;						// Assign the USB stream field
	IO.LCD_stream = &LCD_stream;						// Assign the LCD stream field
	IO.I2C_port1 = &i2cport_1;							// Assign the I2C stream field
	i2cport_1.sdaport = &SDAPORT;						// Assign the SDAPORT field
	i2cport_1.sclport = &SCLPORT;						// Assign the SCLPORT field
	i2cport_1.sdaddr = &SDADDR;							// Assign the SDADDR field
	i2cport_1.sclddr = &SCLDDR;							// Assign the SCLDDR field
	i2cport_1.sdapin = &SDAPIN;							// Assign the SDAPIN field
	i2cport_1.sclpin = &SCLPIN;							// Assign the SCLPIN field
	i2cport_1.sda = SDA;								// Assign the SDA field
	i2cport_1.scl = SCL;								// Assign the SCL field

	SetupHardware();									// Initialize ports, pins and timers
	_delay_ms(512);										// Help USB get recognized on reset
	USB_Init();											// LUFA stuff
	CDC_init();											// LUFA stuff
	sei();												// Enable interrupts
	I2CInit (IO.I2C_port1);								// Initialize I2C Port 1
	LCD_Init(IO.I2C_port1);								// Initialize the LCD display. Requires an already setup I2C port.
	
	cgram_chars[LITTLE_H][0] = 0b01010;					//
	cgram_chars[LITTLE_H][1] = 0b01110;					// This will be the little H
	cgram_chars[LITTLE_H][2] = 0b01010;					// for HIGH range
	cgram_chars[LITTLE_H][3] = 0b00000;					// At address (character) 0
	cgram_chars[LITTLE_H][4] = 0b00000;					//
	cgram_chars[LITTLE_H][5] = 0b00000;					//
	cgram_chars[LITTLE_H][6] = 0b00000;					//
	cgram_chars[LITTLE_H][7] = 0b00000;					//

	cgram_chars[LITTLE_L][0] = 0b00000;					//
	cgram_chars[LITTLE_L][1] = 0b00000;					// This will be the little L
	cgram_chars[LITTLE_L][2] = 0b00000;					// for LOW range
	cgram_chars[LITTLE_L][3] = 0b00000;					// At address (character) 1
	cgram_chars[LITTLE_L][4] = 0b01000;					//
	cgram_chars[LITTLE_L][5] = 0b01000;					//
	cgram_chars[LITTLE_L][6] = 0b01000;					//
	cgram_chars[LITTLE_L][7] = 0b01110;					//
	
	cgram_chars[LOCK_ICON][0] = 0b01110;				//
	cgram_chars[LOCK_ICON][1] = 0b10001;				// This will be the lock
	cgram_chars[LOCK_ICON][2] = 0b10001;				// icon for when the touch
	cgram_chars[LOCK_ICON][3] = 0b11111;				// pad is locked
	cgram_chars[LOCK_ICON][4] = 0b11111;				// At address (character) 2
	cgram_chars[LOCK_ICON][5] = 0b11011;				//
	cgram_chars[LOCK_ICON][6] = 0b11111;				//
	cgram_chars[LOCK_ICON][7] = 0b00000;				//
	
	CGRAM_Setup(cgram_chars, IO.I2C_port1);				// Create custom characters for H and L range and the Lock Icon
	command_count =
		Setup_ScpiCommandsArray_Loadbox_P (commands_P);	// Build the command array (mostly pointers to PROGMEM)
				
	Run_Intro_Screen(IO);								// Splash Screen on LCD
	if (command_count != COMMAND_ARRAY_SIZE)
	{
		LCD_Clear_Home(IO.I2C_port1);
		scpi_prStr_P(PSTR("Array Size "), IO.LCD_stream);
		fprintf(IO.LCD_stream, "%d", command_count);
	}
	
	for (EVER)
	{
		process_USB();									// process_USB() used to be in touch_interrupt.c
		scpi_process_input(str_in, &str_len, commands_P, IO);
		process_gesture(IO);
		update_LCD(IO);
	}
} // END main()

/**************************************************************************
*  LCD_Put_Char is a wrapper to bundle the soft I2C port argument         *
*  so that a single argument function can be passed to the stream handler *
***************************************************************************/
int LCD_Put_Char (char the_char, FILE *stream)
{
		LCD_Display_Char(&i2cport_1, the_char);
		return 0;
}
/****************************************************************************
 *            SetupHardware													*
 *		Configures the board hardware and chip peripherals					*
 ***************************************************************************/
 void SetupHardware(void)
{
	CLKPR = 1 << CLKPCE;						// Enable CLK prescaler change (CLKPCE)
	CLKPR = 0;									// Change CLK prescaler to divide by 1 (CLKPS[3..0])
//	clock_prescale_set(clock_div_1);			// Change CLK prescaler to divide by 1 (CLKPS[3..0]) ---> Uses 12 bytes more ROM
	MCUSR &= ~(1 << WDRF);						// Clear watchdog bit
	wdt_disable();								// Disable watchdog if enabled by bootloader/fuses
//	DDRB = 0; PORTB	= 0; DDRC = 0; PORTC = 0; DDRD = 0; PORTD = 0; DDRE = 0; PORTE = 0; DDRF = 0; PORTF = 0;
	TCCR0A				= 0;					// 8 bit Timer 0 Setup Initialization
	TCCR0B				= (1 << CS01);			// 8MHz, 125ns * 8 prescale = 1us tick time * 256 = 256us overflow. This is used to service USB functions and check touch pads
												// This is the minimum USB process time to avoid buffer problems. See Table 15-9 in the ATMEGA32u4 datasheet.
	TCCR1A				= 0;					// 16-bit Timer 1 Setup Initialization. This timer is used for touch pad timing and is not available otherwise.
	TCCR1B				= (1 << CS11);			// Timer 1 prescaling /8 1us per tick. See Table 16-5 in the ATMEGA32u4 datasheet. 8MHz, 1us * 65,536 = 65.536ms overflow.
	TCCR1B				= 0;					// Timer 1 disabled
	PCMSK1				= 0;					// No pin change interrupts on this bank
	PCMSK0				= 0;					// No pin change interrupts on this bank
	TIMSK0				|= 1 << TOIE0;			// Detect Timer 0 Overflow Changes as Interrupt
	DACPORT				|= LDAC;				// Set the LDAC line - its resting state
	DACDDR				|= LDAC;				// Set the LDAC port line direction to output
	LCD_LED_PORT		|= LCD_LED;				// Set the LCD LED to off by setting the port pin high
	LCD_LED_DDR			|= LCD_LED;				// Set the LCD LED port pin direction to OUTPUT
	I2P5A_PORT			&= ~I2P5A;				// Set the Range Transistor port pin to low
	I2P5A_DDR			|= I2P5A;				// Set the Range Transistor port pin direction to OUTPUT
	LCD_RST_DDR			|= LCD_RST;				// Set the LCD RESET port pin direction to OUTPUT
	LCD_RST_PORT		&= ~LCD_RST;			// LCD display is active reset low. Ensure it's low first.
	LCD_RST_PORT		|= LCD_RST;				// Now high.
	LOW_CUR_ASSIST_DDR	|= LOW_CUR_ASSIST;		// Set the Low Current Assist port pin direction to OUTPUT
	LOW_CUR_ASSIST_PORT	&= ~LOW_CUR_ASSIST;		// Set the Low Current Assist port pin to low	
	ZERO_CURRENT_DDR	|= ZERO_CURRENT;		// Set the Force Zero Current port pin direction to OUTPUT
	ZERO_CURRENT_PORT	&= ~ZERO_CURRENT;		// Set the Force Zero Current port pin to low
	DROPOUT_DTECTR_DDR	&= ~DROPOUT_DTECTR;		// Set dropout detector pin direction to IN and
	DROPOUT_DTECTR_PORT	|= DROPOUT_DTECTR;		// its port pin high enables its pull up
	NTC_ADC_DDR			&= ~NTC_ADC;			// Set the pin to input mode so the thermistor isn't shorted out.
	NTC_ADC_PORT		&= ~NTC_ADC;			// May be redundant, done by the uController already?
	ADC_DIDR			|= ADC_DIDR_BIT;		// Disable the digital input bit on the thermocouple input to the ADC to kill the floating gate.
	ADMUX		 		= AVCC_REF		|		// Set to external reference for thermistor.
						1 << ADLAR 		|		// Set ADC readings to be left justified.
						NTC_ADC_INPUTA;			// Set the MUX[4:0] to point to the thermistor single ended (still needs MUX[5] on ADCSRB).
	ADCSRA				 =	1 << ADEN	|		// Enable the A/D
						1 << ADPS2		|		// ADPS2 - ADPS1 are the
						0 << ADPS1		|		// ADC Clock Divider
						0 << ADPS0;				// Setting just ADPS2 high gives /16: 62.5kHz, 2us/bit, ~27uS conversion
	ADCSRB		 		= NTC_ADC_INPUTB;		// makes up the rest of the MUX[5:0] value.
} // END SetupHardware()
/****************************************************************************
 *	Flashy Intro Screen                                                     *
 ***************************************************************************/
void Run_Intro_Screen (IO_pointers_t IO)
{
	uint16_t duty_cycle = 0, increment = 0;
	uint8_t time = 0;
	
	LCD_Clear_Home(IO.I2C_port1);

	for (uint8_t i = 1; i <= 16 ; i++)
		LCD_Display_Shift_Left(IO.I2C_port1);					// Print on page 1
                                                                //      PAGE 1           PAGE 2
	scpi_prStr_P(PSTR("Current      0nA"), IO.LCD_stream);      //  --------------------------------- 
	LCD_Set_Position(IO.I2C_port1, 2, 1);                       // |Current      0nA|HyperTronix Inc.|
	scpi_prStr_P(PSTR("Heat Sink   21 C"), IO.LCD_stream);      // |Heat Sink   21oC| HTX-9000       |
	LCD_Set_Position(IO.I2C_port1, 2, 15);                      //  --------------------------------- 
	LCD_Display_Char(IO.I2C_port1, DEGREE_BUBBLE_LCD);
	
	LCD_Set_Position(IO.I2C_port1, 1, 17);						// Print on page 2
	fprintf_P(IO.LCD_stream, HyperTronix_Inc);
	LCD_Set_Position(IO.I2C_port1, 2, 21);
	fprintf_P(IO.LCD_stream, HTX_9000);
	
	for (time = 0; time <= 129 ; time++)						// Duty Cycles 1/255 - 1/1
	{	increment = time >> 5;									// Increase by 3%/Step
		if (increment == 0) increment = 1;
		duty_cycle += increment;								// Fade back light on expo
		if (duty_cycle > 255) duty_cycle = 255;
		LCD_Backlight(OFF);	delay_100us(255 - duty_cycle);		// Time for 1 cycle = 25.5ms
		LCD_Backlight(ON);	delay_100us(duty_cycle);
	}
	
//	_delay_ms(5000/3);											// Hold intro screen 2 seconds seems to run 3x specified value
// This has been resposnible for not restarting after boodloader.
	for (uint16_t i = 1; i <= 10000 ; i++)
	{
		delay_100us(10);
		process_USB();
	} 

	for (uint8_t i = 1; i <= 16 ; i++)
	{
		LCD_Display_Shift_Right(IO.I2C_port1);					// slide screen out from right to left
		_delay_ms(10);
	}
}
void delay_100us(uint16_t delay)
{uint16_t i,j;
	for ( i = 0 ; i < delay ; i++ )
  		for (j = 0 ; j < 10 ; j++) asm volatile("nop");
}
